package com.wcpdoc.exam.api.controller;

import com.wcpdoc.base.cache.ProgressBarCache;
import com.wcpdoc.base.entity.Org;
import com.wcpdoc.base.entity.User;
import com.wcpdoc.base.service.OrgService;
import com.wcpdoc.base.service.UserService;
import com.wcpdoc.base.util.CurLoginUserUtil;
import com.wcpdoc.core.context.UserContext;
import com.wcpdoc.core.controller.BaseController;
import com.wcpdoc.core.entity.*;
import com.wcpdoc.core.exception.MyException;
import com.wcpdoc.core.service.OnlineUserService;
import com.wcpdoc.core.util.DateUtil;
import com.wcpdoc.core.util.SpringUtil;
import com.wcpdoc.core.util.ValidateUtil;
import com.wcpdoc.exam.core.cache.AutoMarkCache;
import com.wcpdoc.exam.core.cache.QuestionCache;
import com.wcpdoc.exam.core.entity.*;
import com.wcpdoc.exam.core.entity.ex.ExamInfo;
import com.wcpdoc.exam.core.entity.ex.MockExamInfo;
import com.wcpdoc.exam.core.service.*;
import com.wcpdoc.exam.core.util.QuestionUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;

/**
 * 考试控制层
 * <p>
 * v1.0 zhanghc 2017-06-11 09:13:23
 */
@Controller
@RequestMapping("/api/mockExam")
public class ApiMockExamController extends BaseController {
    private static final Logger log = LoggerFactory.getLogger(ApiMockExamController.class);

    @Resource
    private MockExamService examService;
    @Resource
    private MyMarkService myMarkService;
    @Resource
    private UserService userService;
    @Resource
    private OnlineUserService onlineUserService;
    @Resource
    private MyMockExamService myExamService;
    @Resource
    private MockExamQuestionService examQuestionService;
    @Resource
    private QuestionService questionService;
    @Resource
    private QuestionOptionService questionOptionService;
    @Resource
    private QuestionAnswerService questionAnswerService;
    @Resource
    private MockExamRuleService examRuleService;
    @Resource
    private OrgService orgService;

    /**
     * 考试列表
     * <p>
     * v1.0 zhanghc 2018年10月25日下午9:23:06
     *
     * @return pageOut
     */
    @RequestMapping("/listpage")
    @ResponseBody
    public PageResult listpage() {
        try {
            PageIn pageIn = new PageIn(request);
            if (CurLoginUserUtil.isAdmin()) {// 管理员看所有

            } else if (CurLoginUserUtil.isSubAdmin()) {// 子管理员看自己
                pageIn.addAttr("subAdminUserId", getCurUser().getId());
            } else if (CurLoginUserUtil.isMarkUser()) {// 阅卷用户看（管理或子管理）分配的
                pageIn.addAttr("markUserId", getCurUser().getId());
            } else if (CurLoginUserUtil.isExamUser()) {// 考试用户没有权限
                throw new MyException("无权限");
            }

            PageOut pageOut = examService.getListpage(pageIn);
            for (Map<String, Object> map : pageOut.getList()) {
                if (map.get("sxes") == null) {
                    map.put("sxes", new Integer[0]);
                } else {
                    map.put("sxes", ((String) map.get("sxes")).split(","));
                }
            }

            return PageResultEx.ok().data(pageOut);
        } catch (Exception e) {
            log.error("考试列表错误：", e);
            return PageResult.err();
        }
    }

    /**
     * 模考发布
     * <p>
     * v1.0 zhanghc 2018年11月24日上午9:13:22
     *
     * @param examInfo
     * @return PageResult
     */
    @RequestMapping("/publish")
    @ResponseBody
    public PageResult publish(@RequestBody MockExamInfo examInfo) {
        try {
            ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            RequestContextHolder.setRequestAttributes(requestAttributes, true);// 子线程共享请求属性

            String processBarId = UUID.randomUUID().toString().replaceAll("-", "");
            ProgressBarCache.setProgressBar(processBarId, 0.0, 1.0, "发布开始", HttpStatus.OK.value());// 放在前面，可能的问题为下面的线程执行慢，没有进度数据，前端显示空
            Double processLen = (examInfo.getMockExamUserIds().length + 5) * 1.0;// 校验前数据处理+1，校验数据+1，保存考试+1，保存试卷+1，在业务层完成+1（事务内完成100%可能页面没刷新到），考试用户数量+userNum
            LoginUser loginUser = getCurUser();
            new Thread(new Runnable() {
                public void run() {
                    UserContext.set(loginUser);// 子线程不走springboot拦截器，人工模拟拦截器，线程上绑定当前登录信息
                    try {
                        SpringUtil.getBean(MockExamService.class).publish(examInfo, processBarId);
                        ProgressBarCache.setProgressBar(processBarId, processLen, processLen, "发布成功", HttpStatus.OK.value());// 放在业务层最后一行，进度已经100%，数据还没有完全插入，这时查询数据库时为空
                    } catch (MyException e) {
                        ProgressBarCache.setProgressBar(processBarId, 99.0, 100.0, e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR.value());
                        log.error("发布考试错误：{}", e.getMessage());
                        UserContext.remove();
                    } catch (Exception e) {
                        ProgressBarCache.setProgressBar(processBarId, 99.0, 100.0, e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR.value());
                        log.error("发布考试错误：", e);
                        UserContext.remove();
                    }
                }
            }).start();

            return PageResultEx.ok().data(processBarId);
        } catch (MyException e) {
            log.error("发布考试错误：{}", e.getMessage());
            return PageResult.err().msg(e.getMessage());
        } catch (Exception e) {
            log.error("发布考试错误：", e);
            return PageResult.err();
        }
    }

    /**
     * 考试试卷
     * <p>
     * v1.0 zhanghc 2022年10月25日下午3:30:55
     *
     * @param id
     * @return PageResult
     */
    @RequestMapping("/paper")
    @ResponseBody
    public PageResult paper(Integer id) {
        try {
            MockExam exam = examService.getEntity(id);
            if (!(CurLoginUserUtil.isSelf(exam.getCreateUserId()) || CurLoginUserUtil.isAdmin())) {
                throw new MyException("无操作权限");
            }

            List<Map<String, Object>> examQuestions = new ArrayList<>();
            List<Map<String, Object>> examRules = new ArrayList<>();
            if (exam.getGenType() == 1) {// 人工组卷
                List<MockExamQuestion> _examQuestionList = examQuestionService.getList(id);
                for (MockExamQuestion _examQuestion : _examQuestionList) {
                    Map<String, Object> examQuestion = new HashMap<>();
                    examQuestion.put("type", _examQuestion.getType());
                    if (_examQuestion.getType() == 1) {
                        examQuestion.put("chapterName", _examQuestion.getChapterName());
                        examQuestion.put("chapterTxt", _examQuestion.getChapterTxt());
                    } else {
                        Question _question = QuestionCache.getQuestion(_examQuestion.getQuestionId());// 已关联考试的试题不会改变，缓存起来加速查询。
                        examQuestion.put("questionId", _question.getId());
                        examQuestion.put("questionType", _question.getType());
                        examQuestion.put("markType", _question.getMarkType());
                        examQuestion.put("title", _question.getTitle());
                        examQuestion.put("markOptions", _examQuestion.getMarkOptions());
                        examQuestion.put("score", _examQuestion.getScore());
                        examQuestion.put("scores", _examQuestion.getScores());
                        examQuestion.put("analysis", _question.getAnalysis());

                        List<String> _options = new ArrayList<>();
                        if (QuestionUtil.hasSingleChoice(_question) || QuestionUtil.hasMultipleChoice(_question)) {// 如果是单选或多选，添加选项字段
                            List<QuestionOption> questionOptionList = QuestionCache.getOption(_question.getId());
                            for (QuestionOption questionOption : questionOptionList) {
                                _options.add(questionOption.getOptions());
                            }
                            examQuestion.put("options", _options);
                        }

                        List<QuestionAnswer> _questionAnswerList = QuestionCache.getAnswer(_question.getId());
                        List<Object> _answers = new ArrayList<>();
                        for (QuestionAnswer answer : _questionAnswerList) {
                            if (QuestionUtil.hasSingleChoice(_question) || QuestionUtil.hasTrueFalse(_question)
                                    || (QuestionUtil.hasQA(_question) && QuestionUtil.hasSubjective(_question))) {
                                _answers.add(answer.getAnswer());
                            } else if (QuestionUtil.hasMultipleChoice(_question)) {
                                Collections.addAll(_answers, answer.getAnswer().split(","));
                            } else if (QuestionUtil.hasFillBlank(_question) || (QuestionUtil.hasQA(_question)
                                    && QuestionUtil.hasObjective(_question))) {
                                _answers.add(answer.getAnswer());
                            }
                        }
                        examQuestion.put("answers", _answers);
                    }
                    examQuestions.add(examQuestion);
                }
            } else if (exam.getGenType() == 2) {// 随机组卷
                List<MockExamRule> _examRuleList = examRuleService.getList(id);
                for (MockExamRule _examRule : _examRuleList) {
                    Map<String, Object> examRule = new HashMap<>();
                    examRule.put("type", _examRule.getType());
                    if (_examRule.getType() == 1) {
                        examRule.put("chapterName", _examRule.getChapterName());
                        examRule.put("chapterTxt", _examRule.getChapterTxt());
                    } else {
                        examRule.put("questionTypeId", _examRule.getQuestionTypeId());
                        examRule.put("questionType", _examRule.getQuestionType());
                        examRule.put("markType", _examRule.getMarkType());
                        examRule.put("markOptions", _examRule.getMarkOptions());
                        examRule.put("num", _examRule.getNum());
                        examRule.put("score", _examRule.getScore());
                        examRule.put("scores", _examRule.getQuestionType() == 2 ? _examRule.getScores() : new BigDecimal[0]);// 客观填空等不需要页面处理
                    }
                    examRules.add(examRule);
                }
            }

            List<MyMockExam> _myExamList = myExamService.getList(id);
            List<Integer> examUserIdList = new ArrayList<>();
            for (MyMockExam myExam : _myExamList) {
                examUserIdList.add(myExam.getUserId());
            }

            return PageResultEx.ok()
                    .addAttr("id", exam.getId())
                    .addAttr("name", exam.getName())
                    .addAttr("paperName", exam.getPaperName())
                    .addAttr("markType", exam.getMarkType())// 前端考试回显的时候，根据阅卷类型来判断是否回显阅卷时间
                    .addAttr("startTime", DateUtil.formatDateTime(exam.getStartTime()))
                    .addAttr("endTime", DateUtil.formatDateTime(exam.getEndTime()))
                    .addAttr("markStartTime", ValidateUtil.isValid(exam.getMarkStartTime()) ? DateUtil.formatDateTime(exam.getMarkStartTime()) : null)
                    .addAttr("markEndTime", ValidateUtil.isValid(exam.getMarkEndTime()) ? DateUtil.formatDateTime(exam.getMarkEndTime()) : null)
                    .addAttr("genType", exam.getGenType())
                    .addAttr("showType", exam.getShowType())
                    .addAttr("passScore", exam.getPassScore())
                    .addAttr("anonState", exam.getAnonState())
                    .addAttr("scoreState", exam.getScoreState())
                    .addAttr("rankState", exam.getRankState())
                    .addAttr("sxes", exam.getSxes())
                    .addAttr("state", exam.getState())
                    .addAttr("examQuestions", examQuestions)
                    .addAttr("examRules", examRules)
                    .addAttr("examUserIds", examUserIdList);
        } catch (MyException e) {
            log.error("考试获取错误：{}", e.getMessage());
            return PageResult.err().msg(e.getMessage());
        } catch (Exception e) {
            log.error("考试获取错误：", e);
            return PageResult.err();
        }
    }


    /**
     * 考试删除
     * <p>
     * v1.0 zhanghc 2017-06-11 09:13:23
     *
     * @return pageOut
     */
    @RequestMapping("/del")
    @ResponseBody
    public PageResult del(Integer id) {
        try {
            examService.delEx(id);
            return PageResult.ok();
        } catch (MyException e) {
            log.error("删除考试错误：{}", e.getMessage());
            return PageResult.err().msg(e.getMessage());
        } catch (Exception e) {
            log.error("删除考试错误：", e);
            return PageResult.err();
        }
    }

    /**
     * 考试获取
     * <p>
     * v1.0 zhanghc 2021年12月21日下午4:36:14
     *
     * @param id
     * @return PageResult
     */
    @RequestMapping("/get")
    @ResponseBody
    public PageResult get(Integer id) {
        try {
            MockExam exam = examService.getEntity(id);
            return PageResultEx.ok()
                    .addAttr("id", exam.getId())
                    .addAttr("name", exam.getName())
                    .addAttr("paperName", exam.getPaperName())
                    .addAttr("startTime", ValidateUtil.isValid(exam.getStartTime()) ? DateUtil.formatDateTime(exam.getStartTime()) : null)
                    .addAttr("endTime", ValidateUtil.isValid(exam.getEndTime()) ? DateUtil.formatDateTime(exam.getEndTime()) : null)
                    .addAttr("markStartTime", ValidateUtil.isValid(exam.getMarkStartTime()) ? DateUtil.formatDateTime(exam.getMarkStartTime()) : null)
                    .addAttr("markEndTime", ValidateUtil.isValid(exam.getMarkEndTime()) ? DateUtil.formatDateTime(exam.getMarkEndTime()) : null)
                    .addAttr("markState", exam.getMarkState())
                    .addAttr("scoreState", exam.getScoreState())
                    .addAttr("rankState", exam.getRankState())
                    .addAttr("anonState", exam.getAnonState())
                    .addAttr("passScore", exam.getPassScore())
                    .addAttr("totalScore", exam.getTotalScore())
                    .addAttr("markType", exam.getMarkType())
                    .addAttr("genType", exam.getGenType())
                    .addAttr("sxes", exam.getSxes())
                    .addAttr("state", exam.getState())
                    ;
        } catch (MyException e) {
            log.error("获取考试错误：{}", e.getMessage());
            return PageResult.err().msg(e.getMessage());
        } catch (Exception e) {
            log.error("获取考试错误：", e);
            return PageResult.err();
        }
    }

    /**
     * 考试变更时间
     * <p>
     * v1.0 zhanghc 2022年4月17日下午6:52:08
     *
     * @param id       考试ID
     * @param timeType 时间类型：1：考试开始时间；2：考试结束时间；3：阅卷开始时间；4：阅卷结束时间
     * @param minute   分钟数
     * @return PageResult
     */
    @RequestMapping("/time")
    @ResponseBody
    public PageResult time(Integer id, Integer timeType, Integer minute) {
        try {
            if (!AutoMarkCache.tryWriteLock(id, 2000)) {
                throw new MyException("尝试加写锁失败");
            }
            examService.timeUpdate(id, timeType, minute);
            return PageResult.ok();
        } catch (MyException e) {
            log.error("考试变更时间错误：{}", e.getMessage());
            return PageResult.err().msg(e.getMessage());
        } catch (Exception e) {
            log.error("考试变更时间错误：", e);
            return PageResult.err();
        } finally {
            AutoMarkCache.releaseWriteLock(id);
        }
    }

    /**
     * 阅卷协助
     * <p>
     * v1.0 zhanghc 2023年9月22日下午4:15:24
     *
     * @param id
     * @param markUserIds
     * @return PageResult
     */
    @RequestMapping("/assist")
    @ResponseBody
    public PageResult assist(Integer id, Integer[] markUserIds) {
        try {
            examService.assist(id, markUserIds);
            return PageResult.ok();
        } catch (MyException e) {
            log.error("阅卷协助错误：{}", e.getMessage());
            return PageResult.err().msg(e.getMessage());
        } catch (Exception e) {
            log.error("阅卷协助错误：", e);
            return PageResult.err();
        }
    }

    /**
     * 阅卷用户列表
     * <p>
     * v1.0 zhanghc 2023年9月26日下午5:12:17
     *
     * @param id
     * @return PageResult
     */
    @RequestMapping("/markUserList")
    @ResponseBody
    public PageResult markUserList(Integer id) {
        try {
            // 数据校验
            MockExam exam = examService.getEntity(id);
            if (!(CurLoginUserUtil.isSelf(exam.getCreateUserId()) || CurLoginUserUtil.isAdmin())) {
                throw new MyException("无操作权限");
            }

            // 获取阅卷用户列表
            List<MyMark> myMarkList = myMarkService.getList(id);
            List<Map<String, Object>> resultList = new ArrayList<>();
            for (MyMark myMark : myMarkList) {
                Map<String, Object> result = new HashMap<>();
                User markUser = userService.getEntity(myMark.getMarkUserId());
                result.put("id", markUser.getId());
                result.put("name", markUser.getName());
                resultList.add(result);
            }
            return PageResultEx.ok().data(resultList);
        } catch (MyException e) {
            log.error("阅卷用户列表错误：{}", e.getMessage());
            return PageResult.err().msg(e.getMessage());
        } catch (Exception e) {
            log.error("阅卷用户列表错误：", e);
            return PageResult.err();
        }
    }

    /**
     * 考试用户列表
     * <p>
     * v1.0 zhanghc 2023年9月26日下午5:12:17
     *
     * @param id
     * @return PageResult
     */
    @RequestMapping("/examUserList")
    @ResponseBody
    public PageResult examUserList(Integer id) {
        try {
            // 数据校验
            MockExam exam = examService.getEntity(id);
            if (!(CurLoginUserUtil.isSelf(exam.getCreateUserId()) || CurLoginUserUtil.isAdmin())) {
                throw new MyException("无操作权限");
            }

            // 获取阅卷用户列表
            List<MyMockExam> myExamList = myExamService.getList(id);
            Integer[] userIds = new Integer[myExamList.size()];
            for (int i = 0; i < myExamList.size(); i++) {
                userIds[i] = myExamList.get(i).getUserId();
            }
            List<User> userList = userService.getList(userIds);
            Map<Integer, User> userCache = new HashMap<>();
            for (User user : userList) {
                userCache.put(user.getId(), user);
            }
            List<Org> orgList = orgService.getList();
            Map<Integer, Org> orgCache = new HashMap<>();
            for (Org org : orgList) {
                orgCache.put(org.getId(), org);
            }

            List<Map<String, Object>> resultList = new ArrayList<>();
            for (MyMockExam myExam : myExamList) {
                Map<String, Object> result = new HashMap<>();
                result.put("userId", myExam.getUserId());
                result.put("userName", userCache.get(myExam.getUserId()).getName());
                result.put("orgId", userCache.get(myExam.getUserId()).getOrgId());
                result.put("orgName", orgCache.get(userCache.get(myExam.getUserId()).getOrgId()).getName());
                result.put("myExamState", myExam.getState());
                result.put("myExamMarkState", myExam.getMarkState());
                resultList.add(result);
            }
            return PageResultEx.ok().data(resultList);
        } catch (MyException e) {
            log.error("考试用户列表错误：{}", e.getMessage());
            return PageResult.err().msg(e.getMessage());
        } catch (Exception e) {
            log.error("考试用户列表错误：", e);
            return PageResult.err();
        }
    }

}